/*
 *Copyright (c) 2024 Black Sesame Technologies
 *
 *Licensed under the Apache License, Version 2.0 (the "License");
 *you may not use this file except in compliance with the License.
 *You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *Unless required by applicable law or agreed to in writing, software
 *distributed under the License is distributed on an "AS IS" BASIS,
 *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *See the License for the specific language governing permissions and
 *limitations under the License.
*/
#include "ipc_hw_layer.h"
#include "ipc_hw_impl.h"
#include "ipc_hw_sem.h"
#include "custom_Std_Types.h"
#include "SchM_Msgbx.h"


#define MSGBX_PAYLOAD_MAX_LEN 9
#define MSGBX_WAIT_TIMEOUT_CNT 0xFF

#define MSG_SWITCH1_MAX_PID (0x50)
#define SW1_TO_SW2_LOCK_ID  (0x0)
#define SW2_TO_SW1_LOCK_ID  (0x1)
#define MSG_SEM_INVALID_ID  (0xFF)

#ifndef MSGBX_DELAY_TIME_ADDR
#define MSGBX_DELAY_TIME_ADDR TIMER0_PWM0_BASE_ADDR
#endif

#ifndef MSGBX_TIME_CLOCK
#define MSGBX_TIME_CLOCK 100000000
#endif

#define MODULE_NAME "Msgbx_HwLayer"
#if 0
#define MSGBX_LOG(fmt, ...) printf(fmt, ##__VA_ARGS__)
#else
#include "../../../library/printf_inc.h"
#define MSGBX_LOG(fmt, ...) asm("nop");
 //#define MSGBX_LOG(fmt, ...) hprintf(DEBUG, fmt, ##__VA_ARGS__)
#endif

#define MSGBX_LOG_DEBUG(format, ...)                                            \
do {                                                                            \
MSGBX_LOG("[D][%s][%s]:" format "\r\n", MODULE_NAME, __func__, ##__VA_ARGS__);  \
} while (false)

#define MSGBX_LOG_STRING(format, ...)                                  \
do {                                                                   \
MSGBX_LOG(format, ##__VA_ARGS__);                                      \
} while (false)

typedef union
{
    struct
    {
        uint32_t pid:8;
        uint32_t cid:8;
        uint32_t len:4;
        uint32_t is_64bit: 1;
        uint32_t nonsec: 1;
        uint32_t res:10;
    }bit;
    uint32_t data;
}msg_head_u;

typedef struct
{
    msg_head_u header;
    uint32_t payload[MSGBX_PAYLOAD_MAX_LEN];
}msgbox_data_t;

static rw_msg_t recv_msg_data;

extern int32_t ipc_hw_recv_msg_notify(const uint8_t endid, const uint8_t fid, const rw_msg_t *msg);

static uint32 msgbx_get_sem_lock(uint8 pid, uint8 cid)
{
    uint8 pid_high = pid & 0xF0; 
    uint8 cid_high = cid & 0xF0;
    uint8 sem_id = MSG_SEM_INVALID_ID;

    if((pid_high <= MSG_SWITCH1_MAX_PID) && (cid_high > MSG_SWITCH1_MAX_PID))
    {
        sem_id = SW1_TO_SW2_LOCK_ID;
    } 
    else if((pid_high > MSG_SWITCH1_MAX_PID) && (cid_high <= MSG_SWITCH1_MAX_PID))
    {
        sem_id = SW2_TO_SW1_LOCK_ID;
    } 
    else 
    {
        sem_id = MSG_SEM_INVALID_ID;
    }

    if(sem_id != MSG_SEM_INVALID_ID)
    {
        get_sem_lock(sem_id);
    }

    return(sem_id);
}

static void msgbx_release_sem_lock(uint8 sem_id)
{
    if(sem_id != MSG_SEM_INVALID_ID)
    {
        release_sem_lock(sem_id);
    }
}


void msgbx_delay_us(uint32 us)
{
    uint32 ticks;
    uint32 told,tnow,reload,tcnt=0;

    if(!REG_BIT_GET32(MSGBX_DELAY_TIME_ADDR+0x08, BIT0)) /*timer is not enable.*/
    {
        if(REG_READ32(MSGBX_DELAY_TIME_ADDR) == 0)
        {
            REG_WRITE32(MSGBX_DELAY_TIME_ADDR, 0xFFFFFFFE);
        }
        REG_BIT_SET32(MSGBX_DELAY_TIME_ADDR+0x08, BIT0);
    }
    reload = REG_READ32(MSGBX_DELAY_TIME_ADDR);
    ticks = us * (MSGBX_TIME_CLOCK / 1000000);
    told = REG_READ32(MSGBX_DELAY_TIME_ADDR+0x04);

    while(1)
    {
        tnow = REG_READ32(MSGBX_DELAY_TIME_ADDR+0x04);
        if(tnow != told)
        {         
            if(tnow < told)
                tcnt+=told-tnow;
            else 
                tcnt+=reload-tnow+told;
                told=tnow;
            if(tcnt>=ticks)
                break;
        } 
    }     
}

static void msgbx_set_de_filter_addr(uint32_t de_fileter_base, uint32_t start_addr, uint32_t end_addr)
{
    uint32_t reg_data = 0;

    reg_data = (((start_addr << END_DEFAULT_RXFIFO_ADDRR_RX_FIFO_ST_ADDR_SHIFT_U32) & END_DEFAULT_RXFIFO_ADDRR_RX_FIFO_ST_ADDR_U32) | 
            ((end_addr << END_DEFAULT_RXFIFO_ADDRR_RX_FIFO_END_ADDR_SHIFT_U32) & END_DEFAULT_RXFIFO_ADDRR_RX_FIFO_END_ADDR_U32));
    REG_WRITE32(de_fileter_base + END_DEFAULT_RXFIFO_ADDRR_OFFSET, reg_data);
}

static void msgbx_set_de_filter_pid(uint32_t de_fileter_base, uint32_t start_pid, uint32_t end_pid, uint32_t invert)
{
    uint32_t reg_data = 0;

    reg_data = ((((end_pid << END_DEFAULT_MSGH_PIDF_CFGR_RX_PID_END_SHIFT_U32) & END_DEFAULT_MSGH_PIDF_CFGR_RX_PID_END_U32) | 
                ((start_pid << END_DEFAULT_MSGH_PIDF_CFGR_RX_PID_ST_SHIFT_U32) & END_DEFAULT_MSGH_PIDF_CFGR_RX_PID_ST_U32) | 
                ((invert << END_DEFAULT_MSGH_PIDF_CFGR_RX_PID_FILTER_INVERT_SHIFT_U32) & END_DEFAULT_MSGH_PIDF_CFGR_RX_PID_FILTER_INVERT_U32)) |
                END_DEFAULT_MSGH_PIDF_CFGR_RX_FILTER_EN_U32);
    REG_WRITE32(de_fileter_base + END_DEFAULT_MSGH_PIDF_CFGR_OFFSET, reg_data);
}

static void msgbx_set_filter_addr(uint32_t flt_reg_base, uint32_t start_addr, uint32_t end_addr)
{
    uint32_t reg_data = 0;
    reg_data = ((((start_addr << END_FILTER1_RXFIFO_ADDRR_RX_FIFO_ST_ADDR_SHIFT_U32) & END_FILTER1_RXFIFO_ADDRR_RX_FIFO_ST_ADDR_U32) | 
                ((end_addr << END_FILTER1_RXFIFO_ADDRR_RX_FIFO_END_ADDR_SHIFT_U32) & END_FILTER1_RXFIFO_ADDRR_RX_FIFO_END_ADDR_U32)) |
                END_FILTER1_RXFIFO_ADDRR_RX_FILTER_EN_U32);
    REG_WRITE32(flt_reg_base + END_FILTER1_RXFIFO_ADDRR_OFFSET, reg_data);
}

static void msgbx_ctrl_filter_addr(uint32_t flt_reg_base, uint8_t enable)
{
    (enable != 0) ? (REG_BIT_SET32(flt_reg_base + END_FILTER1_RXFIFO_ADDRR_OFFSET, BIT31)) : 
                    (REG_BIT_CLEAR32(flt_reg_base + END_FILTER1_RXFIFO_ADDRR_OFFSET, BIT31));
}

static void msgbx_set_filter_pid(uint32_t flt_reg_base, uint32_t start_pid, uint32_t end_pid, uint32_t invert)
{
    uint32_t reg_data = 0;

    reg_data = ((((end_pid << END_FILTER1_MSGH_PIDF_CFGR_RX_PID_END_SHIFT_U32) & END_FILTER1_MSGH_PIDF_CFGR_RX_PID_END_U32) | 
                ((start_pid << END_FILTER1_MSGH_PIDF_CFGR_RX_PID_ST_SHIFT_U32) & END_FILTER1_MSGH_PIDF_CFGR_RX_PID_ST_U32) | 
                ((invert << END_FILTER1_MSGH_PIDF_CFGR_RX_PID_FILTER_INVERT_SHIFT_U32) & END_FILTER1_MSGH_PIDF_CFGR_RX_PID_FILTER_INVERT_U32)) |
                END_FILTER1_MSGH_PIDF_CFGR_RX_PID_FILTER_EN_U32);
    REG_WRITE32(flt_reg_base + END_FILTER1_MSGH_PIDF_CFGR_OFFSET, reg_data);
}

static void msgbx_ctrl_filter_pid(uint32_t flt_reg_base, uint8_t enable)
{
    (enable != 0) ? (REG_BIT_SET32(flt_reg_base+END_FILTER1_MSGH_PIDF_CFGR_OFFSET, BIT31)) : 
                        (REG_BIT_CLEAR32(flt_reg_base+END_FILTER1_MSGH_PIDF_CFGR_OFFSET,BIT31));
}

static void msgbx_set_filter_len(uint32_t flt_reg_base, uint32_t start_len, uint32_t end_len, uint32_t invert)
{
    uint32_t reg_data = 0;

    reg_data = ((((end_len << END_FILTER1_MSGH_LENF_CFGR_RX_LEN_END_SHIFT_U32) & END_FILTER1_MSGH_LENF_CFGR_RX_LEN_END_U32) | 
                ((start_len << END_FILTER1_MSGH_LENF_CFGR_RX_LEN_ST_SHIFT_U32) & END_FILTER1_MSGH_LENF_CFGR_RX_LEN_ST_U32) | 
                ((invert << END_FILTER1_MSGH_LENF_CFGR_RX_LEN_FILTER_INVERT_SHIFT_U32) & END_FILTER1_MSGH_LENF_CFGR_RX_LEN_FILTER_INVERT_U32)) |
                END_FILTER1_MSGH_LENF_CFGR_RX_LEN_FILTER_EN_U32);
    REG_WRITE32(flt_reg_base + END_FILTER1_MSGH_LENF_CFGR_OFFSET, reg_data);
}

static void msgbx_ctrl_filter_len(uint32_t flt_reg_base, uint8_t enable)
{
    (enable != 0) ? (REG_BIT_SET32(flt_reg_base + END_FILTER1_MSGH_LENF_CFGR_OFFSET, BIT31)) : 
                    (REG_BIT_CLEAR32(flt_reg_base + END_FILTER1_MSGH_LENF_CFGR_OFFSET, BIT31));
}

static void msgbx_set_filter_res_mask(uint32_t flt_reg_base, uint32_t mask_bit)
{
    REG_WRITE32((flt_reg_base + END_FILTER1_MSGH_RESF_MASKR_OFFSET), 
                ((mask_bit << END_FILTER1_MSGH_RESF_MASKR_RX_RESERVED_FILTER_MASK_SHIFT_U32) & END_FILTER1_MSGH_RESF_MASKR_RX_RESERVED_FILTER_MASK_U32));
}

static void msgbx_set_filter_res_Hmask(uint32_t flt_reg_base, uint32_t Hmask_bit)
{
    REG_WRITE32((flt_reg_base + END_FILTER1_MSGH_RESF_MASKHR_OFFSET), Hmask_bit);
}

static void msgbx_set_filter_res_min(uint32_t flt_reg_base, uint32_t min_value)
{
    REG_WRITE32((flt_reg_base + END_FILTER1_MSGH_RESF_MINR_OFFSET), 
            ((min_value << END_FILTER1_MSGH_RESF_MINR_RX_RES_MIN_SHIFT_U32) & END_FILTER1_MSGH_RESF_MINR_RX_RES_MIN_U32));
}

static void msgbx_set_filter_res_Hmin(uint32_t flt_reg_base, uint32_t Hmin_value)
{
    REG_WRITE32((flt_reg_base + END_FILTER1_MSGH_RESF_MINHR_OFFSET), Hmin_value);
}

static void msgbx_set_filter_res_max(uint32_t flt_reg_base, uint32_t max_value)
{
    REG_WRITE32((flt_reg_base + END_FILTER1_MSGH_RESF_MAXR_OFFSET),
                ((max_value << END_FILTER1_MSGH_RESF_MAXR_RX_RES_MAX_SHIFT_U32) & END_FILTER1_MSGH_RESF_MAXR_RX_RES_MAX_U32));
}

static void msgbx_set_filter_res_Hmax(uint32_t flt_reg_base, uint32_t Hmax_value)
{
    REG_WRITE32((flt_reg_base + END_FILTER1_MSGH_RESF_MAXHR_OFFSET), Hmax_value);
}

static void msgbx_set_filter_payload_mask(uint32_t flt_reg_base, uint32_t payload_cnt, uint32_t mask_bit)
{
    if(payload_cnt <= 2)
    {
        REG_WRITE32(((flt_reg_base + END_FILTER1_MSGP1_MASKR_OFFSET) + (payload_cnt*0x30)), mask_bit);
    }
    else if(payload_cnt < MSGBX_FILTER_END_NUM)
    {
        REG_WRITE32(((flt_reg_base + END_FILTER1_MSGP1_MASKR_OFFSET) + (payload_cnt*0x30) + 0x08), mask_bit);
    }
}

static void msgbx_set_filter_payload_Hmask(uint32_t flt_reg_base, uint32_t payload_cnt, uint32_t Hmask_bit)
{
    if(payload_cnt <= 2)
    {
        REG_WRITE32(((flt_reg_base + END_FILTER1_MSGP1_MASKHR_OFFSET) + (payload_cnt*0x30)), Hmask_bit);
    }
    else if(payload_cnt < MSGBX_FILTER_END_NUM)
    {
        REG_WRITE32(((flt_reg_base + END_FILTER1_MSGP1_MASKHR_OFFSET) + (payload_cnt*0x30) + 0x08), Hmask_bit);
    }
}

static void msgbx_set_filter_payload_min(uint32_t flt_reg_base, uint32_t payload_cnt, uint32_t min_value)
{
    if(payload_cnt <= 2)
    {
        REG_WRITE32(((flt_reg_base + END_FILTER1_MSGP1_MINR_OFFSET) + (payload_cnt*0x30)),min_value);
    }
    else if(payload_cnt < MSGBX_FILTER_END_NUM)
    {
        REG_WRITE32(((flt_reg_base + END_FILTER1_MSGP1_MINR_OFFSET) + (payload_cnt*0x30) + 0x08),min_value);
    }
}

static void msgbx_set_filter_payload_Hmin(uint32_t flt_reg_base, uint32_t payload_cnt, uint32_t Hmin_value)
{
    if(payload_cnt <= 2)
    {
        REG_WRITE32(((flt_reg_base + END_FILTER1_MSGP1_MINHR_OFFSET) + (payload_cnt*0x30)), Hmin_value);
    }
    else if(payload_cnt < MSGBX_FILTER_END_NUM)
    {
        REG_WRITE32(((flt_reg_base + END_FILTER1_MSGP1_MINHR_OFFSET) + (payload_cnt*0x30) + 0x08), Hmin_value);
    }
}

static void msgbx_set_filter_payload_max(uint32_t flt_reg_base, uint32_t payload_cnt, uint32_t max_value)
{
    if(payload_cnt < 2)
    {
        REG_WRITE32(((flt_reg_base + END_FILTER1_MSGP1_MAXR_OFFSET) + (payload_cnt*0x30)), max_value);
    }
    else if(payload_cnt < MSGBX_FILTER_END_NUM)
    {
        REG_WRITE32(((flt_reg_base + END_FILTER1_MSGP1_MAXR_OFFSET) + (payload_cnt*0x30) + 0x08), max_value);
    }
}

static void msgbx_set_filter_payload_Hmax(uint32_t flt_reg_base, uint32_t payload_cnt, uint32_t Hmax_value)
{
    if(payload_cnt < 2)
    {
        REG_WRITE32(((flt_reg_base + END_FILTER1_MSGP1_MAXHR_OFFSET) + (payload_cnt*0x30)), Hmax_value);
    }
    else if(payload_cnt < MSGBX_FILTER_END_NUM)
    {
        REG_WRITE32(((flt_reg_base + END_FILTER1_MSGP1_MAXHR_OFFSET) + (payload_cnt*0x30) + 0x08), Hmax_value);
    }
}

/**
 * @brief get msgbx rxfifo status
 * 
 * @param flt_reg_base 
 * @param fid filter num 0:default filter
 * @return uint32_t The Number of messages that have not been handled
 */
static uint32_t msgbx_get_rxfifo_status(uint32_t flt_reg_base, uint32_t fid)
{
    uint32_t ret = 0;

    if(0 == fid)
    {
        ret = REG_READ32(flt_reg_base + END_DEFAULT_RXFIFO_STATUSR_OFFSET);
    }
    else if(MSGBX_FILTER_END_NUM > fid)
    {
        ret = REG_READ32(flt_reg_base + fid*0x200 + END_FILTER_RXFIFO_STATUSR_OFFSET);
    }

    return(ret);
}

/**
 * @brief msgbx isr process hanedle
 * 
 * @param intr_state intr status
 * @param fid   filter num
 */
static void msgbx_isr_process(uint32_t fid)
{
    uint32_t msg_num = 0;
    uint32_t reg_data = 0;
    uint32_t reg_base = 0;
    uint32_t read_addr = 0;
    uint32_t read_len = 0, i = 0;
    msgbox_data_t *msg_ptr;

    reg_base = MSGBX_BASE_ADDR + (fid * 0x200);
    if(0 == fid)    /*defaulte filter num */
    {  
        /*read intr status*/
        reg_data = REG_READ32(reg_base+END_INTRR_OFFSET);
        if(reg_data & END_INTRR_DEFAULT_RX_THRS_INTR_U32)
        {
            msg_num = REG_READ32(MSGBX_BASE_ADDR + END_DEFAULT_RXFIFO_STATUSR_OFFSET);
            for (; msg_num > 0; msg_num--)
            {
                read_addr = reg_base + MSGBX_END_DE_RXFIFO_OFFSET;
                msg_ptr = (msgbox_data_t *)&recv_msg_data;
                msg_ptr->header.data = REG_READ32(read_addr);
                read_len = msg_ptr->header.bit.is_64bit ? ((msg_ptr->header.bit.len << 1) + 1) : (msg_ptr->header.bit.len);
                MSGBX_LOG_DEBUG("recv data1 = 0x%08x ", msg_ptr->header.data);
                if(read_len <= MSGBX_PAYLOAD_MAX_LEN)
                {
                    for (i = 0; i < read_len; i++)
                    {
                        msg_ptr->payload[i] = REG_READ32(read_addr);
                        MSGBX_LOG_STRING("0x%08x ", msg_ptr->payload[i]);
                    }
					MSGBX_LOG_STRING("\r\n");

                    if(!msg_ptr->header.bit.is_64bit) /*32bit system need trans to 64bit system len.*/
                    {
                        recv_msg_data.header.len = (recv_msg_data.header.len >> 1);
                    }

                    ipc_hw_recv_msg_notify(0, fid, &recv_msg_data);
                }
            }
        }

#ifdef IPC_STATE_MGT_ENABLE 
        if(reg_data & (END_INTRR_DEFAULT_RX_UNDERFLOW_INTR_U32 | 
                    END_INTRR_DEFAULT_RX_OVERFLOW_INTR_U32 | END_INTRR_TX_OVERFLOW_INTR_U32))
        {
            ipc_hw_err_msg_notify(0, fid);
        }
#endif
        /*clean intr status*/
        REG_WRITE32((reg_base+END_STATUSR_OFFSET), reg_data);
    }
    else if(MSGBX_FILTER_END_NUM > fid)
    {
        reg_data = REG_READ32(reg_base + END_FILTER_INTRR_OFFSET);
        if(reg_data & END_FILTER_INTRR_FILTER_THRS_INTR_U32)
        {
            msg_num = REG_READ32(reg_base + END_FILTER_RXFIFO_STATUSR_OFFSET);
            for (; msg_num > 0; msg_num--)
            {
                read_addr = reg_base + MSGBX_END_DE_RXFIFO_OFFSET;
                msg_ptr = (msgbox_data_t *)&recv_msg_data;
                msg_ptr->header.data = REG_READ32(read_addr);
                read_len = msg_ptr->header.bit.is_64bit ? ((msg_ptr->header.bit.len << 1) + 1) : (msg_ptr->header.bit.len);
                MSGBX_LOG_DEBUG("recv data2 =0x%0x ",msg_ptr->header.data);
                if(read_len <= MSGBX_PAYLOAD_MAX_LEN)
                {
                    for (i = 0; i < read_len; i++)
                    {
                        msg_ptr->payload[i] = REG_READ32(read_addr);
						MSGBX_LOG_STRING("0x%08x ",i, msg_ptr->payload[i]);
                    }
					MSGBX_LOG_STRING("\r\n");

                    if(!msg_ptr->header.bit.is_64bit) /*32bit system need trans to 64bit system len.*/
                    {
                        recv_msg_data.header.len = (recv_msg_data.header.len >> 1);
                    }
                    ipc_hw_recv_msg_notify(0, fid, &recv_msg_data);
                }
            }
        }
#ifdef IPC_STATE_MGT_ENABLE 
        if(reg_data & (END_FILTER_INTRR_FILTER_UNDERFLOW_INTR_U32 | 
                    END_FILTER_INTRR_FILTER_OVERFLOW_INTR_U32))
        {
            ipc_hw_err_msg_notify(0, fid);
        }
#endif
        /*clean intr status*/
        REG_WRITE32((reg_base+END_FILTER_STATUSR_OFFSET), reg_data);
    }
}

/**
 * @brief 
 * 
 * @param base_reg msgbx base reg aadr
 * @param header message header
 * @return int32_t 0:invalid, 1: valid
 */
static int32_t msgbx_header_is_valid(uint32_t base_reg, msg_head_u *header)
{
    int32_t ret = -1;
    uint8_t pid = (REG_READ32(base_reg) & 0xFF);

    if((header->bit.cid != pid) && 
        (header->bit.pid == pid) && 
        (header->bit.len <= 9))
    {
        switch (header->bit.cid)
        {
            #define MSG_END_MACRO(id, num) case id:
                PID_MSG_END
            #undef MSG_END_MACRO
                ret = 0;
            break;
        default:
                ret = -1;
            break;
        }
    }

    return(ret);
}

/**
 * @brief msgbx hw init function
 * 
 * @param msgbx_param 
 * @return int32_t 
 */
extern unsigned int get_int_num_by_reg_base(unsigned int reg_base);
extern void cpu_gicd_set_enable(unsigned int intr_id);
int32_t msgbx_hw_init(const uint8_t endid, const ipc_init_params_t *msgbx_param)
{
    uint32_t reg_data = 0;

    uint32_t int_base_num = 0;
    uint32_t filter_num =0;
    int_base_num = get_int_num_by_reg_base(MSGBX_BASE_ADDR + filter_num*0x200);
    cpu_gicd_set_enable(int_base_num);

    endid;
    msgbx_set_de_filter_addr(MSGBX_BASE_ADDR, DEF_FILTER_ST_ADDR, 
                                    DEF_FILTER_END_ADDR);
    msgbx_set_de_filter_pid(MSGBX_BASE_ADDR, DEF_FILTER_PID_ST, 
                                    DEF_FILTER_PID_END, DEF_FILTER_PID_INVERT);
    REG_WRITE32(MSGBX_BASE_ADDR+END_DEFAULT_RXTHRS_CFGR_OFFSET, DEF_FILTER_THRS);
    if(msgbx_param != NULL)
    {
#ifdef IPC_STATE_MGT_ENABLE 
        if(msgbx_param->msgbx_end_mgt_flag)
        {
            reg_data |= (msgbx_param->msgbx_end_mgt_flag & (END_INTRR_DEFAULT_RX_UNDERFLOW_INTR_U32 | 
                        END_INTRR_DEFAULT_RX_OVERFLOW_INTR_U32 | END_INTRR_TX_OVERFLOW_INTR_U32));
        }
#endif
    }
    reg_data |= END_ENR_DEFAULT_RX_THRS_INTR_EN_U32;
    REG_BIT_SET32(MSGBX_BASE_ADDR+END_ENR_OFFSET, reg_data);
}

/**
 * @brief msgbx hw deinit
 * 
 * @return int32_t 
 */
int32_t msgbx_hw_deinit(const uint8_t endid)
{

}

/**
 * @brief get msgbx info
 * 
 * @param hw_info 
 * @return int32_t 
 */
int32_t msgbx_hw_get_info(const uint8_t endid, msgbx_hw_info_t *hw_info)
{
    uint32_t reg_data = 0;

    endid;
    reg_data = REG_READ32(MSGBX_BASE_ADDR+MSGBX_END_CONFIG_OFFSET+MSGBX_END_ABLT_R_OFFSET);
    hw_info->mbx_flt_cnt = (reg_data & MSGBX_END_ABLT_R_FILTER_NUM_U32) >> MSGBX_END_ABLT_R_FILTER_NUM_SHIFT_U32;
    hw_info->is_64_bit = (reg_data  & MSGBX_END_ABLT_R_IS_64_BIT_U32) >> MSGBX_END_ABLT_R_IS_64_BIT_SHIFT_U32;
    hw_info->mbx_end_id = (reg_data & MSGBX_END_ABLT_R_MY_ID_U32) >> MSGBX_END_ABLT_R_MY_ID_SHIFT_U32;

    reg_data = REG_READ32(MSGBX_BASE_ADDR+MSGBX_END_CONFIG_OFFSET+MSGBX_END_ABLT2_R_OFFSET);
    hw_info->mbx_txfifo_depth = (reg_data & MSGBX_END_ABLT2_R_TX_FIFO_DEPTH_U32) >> MSGBX_END_ABLT2_R_TX_FIFO_DEPTH_SHIFT_U32;
    hw_info->mbx_rxfifo_depth = (reg_data & MSGBX_END_ABLT2_R_RX_FIFO_DEPTH_U32) >> MSGBX_END_ABLT2_R_RX_FIFO_DEPTH_SHIFT_U32;

    hw_info->mbx_version = REG_READ32(MSGBX_BASE_ADDR+MSGBX_END_CONFIG_OFFSET+VERSIONR_OFFSET);

    return(0);
}


int32_t msgbx_hw_set_rule_pid(const uint8_t flt_id, const ipc_flt_rule_type_t *rule)
{
    int32_t ret = -1;

    switch (flt_id)
    {
    case 0:
        msgbx_set_de_filter_addr(MSGBX_BASE_ADDR, DEF_FILTER_ST_ADDR, DEF_FILTER_END_ADDR);
        REG_WRITE32((MSGBX_BASE_ADDR+END_DEFAULT_RXTHRS_CFGR_OFFSET), DEF_FILTER_THRS);
        msgbx_set_de_filter_pid(MSGBX_BASE_ADDR, rule->mbx_rx_pid_st, 
                                    rule->mbx_rx_pid_end, rule->mbx_pid_flt_invert);
        REG_BIT_SET32(MSGBX_BASE_ADDR + END_ENR_OFFSET, (END_ENR_DEFAULT_RX_UNDERFLOW_INTR_EN_U32 | 
                        END_ENR_DEFAULT_RX_OVERFLOW_INTR_EN_U32 | END_ENR_TX_OVERFLOW_INTR_EN_U32));
        ret = 0;
        break;
    case 1:
        msgbx_set_filter_addr(MSGBX_BASE_ADDR+MSGBX_END_FLT1_CSR_OFFSET, MSGBX_FLT1_ST_ADDR, MSGBX_FLT1_END_ADDR);
        REG_WRITE32((MSGBX_BASE_ADDR+MSGBX_END_FLT1_CSR_OFFSET+END_FILTER_THRS_CFGR_OFFSET), MSGBX_FLT1_THRS);
        msgbx_set_filter_pid(MSGBX_BASE_ADDR+MSGBX_END_FLT1_CSR_OFFSET, rule->mbx_rx_pid_st, 
                                    rule->mbx_rx_pid_end, rule->mbx_pid_flt_invert);
        REG_BIT_SET32(MSGBX_BASE_ADDR + 0x200 + END_FILTER_ENR_OFFSET, 
                    (END_FILTER_ENR_FILTER_UNDERFLOW_INTR_EN_U32 | END_FILTER_ENR_FILTER_OVERFLOW_INTR_EN_U32));
        ret = 0;
        break;
    case 2:
        msgbx_set_filter_addr(MSGBX_BASE_ADDR+MSGBX_END_FLT2_CSR_OFFSET, MSGBX_FLT2_ST_ADDR, MSGBX_FLT2_END_ADDR);
        REG_WRITE32((MSGBX_BASE_ADDR+MSGBX_END_FLT2_CSR_OFFSET+END_FILTER_THRS_CFGR_OFFSET), MSGBX_FLT2_THRS);
        msgbx_set_filter_pid(MSGBX_BASE_ADDR+MSGBX_END_FLT2_CSR_OFFSET, rule->mbx_rx_pid_st, 
                                    rule->mbx_rx_pid_end, rule->mbx_pid_flt_invert);
        REG_BIT_SET32(MSGBX_BASE_ADDR + 0x400 + END_FILTER_ENR_OFFSET, 
                    (END_FILTER_ENR_FILTER_UNDERFLOW_INTR_EN_U32 | END_FILTER_ENR_FILTER_OVERFLOW_INTR_EN_U32));
        ret = 0;
        break;
    case 3:
        msgbx_set_filter_addr(MSGBX_BASE_ADDR+MSGBX_END_FLT3_CSR_OFFSET, MSGBX_FLT3_ST_ADDR, MSGBX_FLT3_END_ADDR);
        REG_WRITE32((MSGBX_BASE_ADDR+MSGBX_END_FLT3_CSR_OFFSET+END_FILTER_THRS_CFGR_OFFSET), MSGBX_FLT3_THRS);
        msgbx_set_filter_pid(MSGBX_BASE_ADDR+MSGBX_END_FLT3_CSR_OFFSET, rule->mbx_rx_pid_st, 
                                    rule->mbx_rx_pid_end, rule->mbx_pid_flt_invert);
        REG_BIT_SET32(MSGBX_BASE_ADDR + 0x600 + END_FILTER_ENR_OFFSET, 
                    (END_FILTER_ENR_FILTER_UNDERFLOW_INTR_EN_U32 | END_FILTER_ENR_FILTER_OVERFLOW_INTR_EN_U32));
        ret = 0;
        break;
    default:
        break;
    }
    return(ret);
}

int32_t msgbx_hw_set_rule_len(const uint8_t flt_id, const msgbx_flt_rule_len_t *rule)
{
    uint32_t ret = -1;

    if(0 < flt_id && flt_id < MSGBX_FILTER_END_NUM)
    {
        msgbx_set_filter_len(MSGBX_BASE_ADDR+flt_id*0x200, rule->mbx_rx_len_st, 
                                rule->mbx_rx_len_end, rule->mbx_len_flt_invert);
        ret = 1;
    }

    return(ret);
}

int32_t msgbx_hw_set_rule_user(const uint8_t flt_id, const msgbx_flt_rule_user_t *rule)
{
    uint32_t ret = -1;
    uint32_t offset = 0;

    if(0 < flt_id && flt_id < MSGBX_FILTER_END_NUM)
    {
        switch (rule->cfg_loc)
        {
        case IPC_FLT_RULE_HEADER:
            msgbx_set_filter_res_mask((MSGBX_BASE_ADDR+flt_id*0x200), rule->tx_reserved_filter_mask);
            msgbx_set_filter_res_min((MSGBX_BASE_ADDR+flt_id*0x200), rule->rx_res_min);
            msgbx_set_filter_res_max((MSGBX_BASE_ADDR+flt_id*0x200), rule->rx_res_max);
            REG_BIT_SET32((MSGBX_BASE_ADDR+flt_id*0x200+0x110), 
                ((rule->msgh_flilter_invert << END_FILTER1_RXFIFO_CFGR_MSGH_FLILTER_INVERT_SHIFT_U32) | END_FILTER1_RXFIFO_CFGR_MSGH_FLILTER_EN_U32));
                ret = 0;
            break;
        case IPC_FLT_RULE_PAY1:
        case IPC_FLT_RULE_PAY2:
        case IPC_FLT_RULE_PAY3:
        case IPC_FLT_RULE_PAY4:
            offset = (rule->cfg_loc-IPC_FLT_RULE_PAY1);
            msgbx_set_filter_payload_mask((MSGBX_BASE_ADDR+flt_id*0x200), offset, rule->tx_reserved_filter_mask);
            msgbx_set_filter_payload_min((MSGBX_BASE_ADDR+flt_id*0x200), offset, rule->rx_res_min);
            msgbx_set_filter_payload_max((MSGBX_BASE_ADDR+flt_id*0x200), offset, rule->rx_res_max);
            REG_BIT_SET32((MSGBX_BASE_ADDR+flt_id*0x200+0x110),
                ((rule->msgh_flilter_invert << (END_FILTER1_RXFIFO_CFGR_MSGP1_FILTER_INVERT_SHIFT_U32+offset)) 
                | (1 << (END_FILTER1_RXFIFO_CFGR_MSGP1_FILTER_EN_SHIFT_U32 + offset))));
            ret = 0;
            break;
        default:
            break;
        }
    }

    return(ret);
}

/**
 * @brief msgbx hw config filter
 * 
 * @param flt_id 
 * @param rule 
 * @return int32_t 
 */
int32_t msgbx_hw_set_flt_cfg(const uint8_t endid, const uint8_t flt_id, const msgbx_flt_rule_cfg_t *rule)
{
    int32_t ret = -1;

    endid;
    switch (rule->cfg_type)
    {
    case IPC_FLT_RULE_PID:
        ret = msgbx_hw_set_rule_pid(flt_id, &rule->rule_pid);
        break;
    case IPC_FLT_RULE_LEN:
        ret = msgbx_hw_set_rule_len(flt_id, &rule->rule_len);
        break;
    case IPC_FLT_RULE_USER:
        ret = msgbx_hw_set_rule_user(flt_id, &rule->rule_user);
        break;
    default:
        break;
    }

    return(ret);
}

/**
 * @brief msgbx hw clear filter config
 * 
 * @param flt_id 
 * @return int32_t 
 */
int32_t msgbx_hw_clr_flt_cfg(const uint8_t endid, const uint8_t flt_id)
{
    if(flt_id == 0)
    {
        REG_BIT_CLEAR32(MSGBX_BASE_ADDR+END_DEFAULT_MSGH_PIDF_CFGR_OFFSET, 
                            END_DEFAULT_MSGH_PIDF_CFGR_RX_FILTER_EN_U32);
    }
    else if(flt_id < MSGBX_FILTER_END_NUM)
    {
        REG_BIT_CLEAR32(MSGBX_BASE_ADDR+flt_id*0x200+END_FILTER1_RXFIFO_ADDRR_OFFSET, 
                            END_FILTER1_RXFIFO_ADDRR_RX_FILTER_EN_U32);
    }
}

/**
 * @brief get msgbx hw config filter
 * 
 * @param flt_id 
 * @param rule 
 * @return int32_t 
 */
int32_t msgbx_hw_get_flt_info(const uint8_t endid, const uint8_t flt_id, msgbx_flt_rule_cfg_t *info)
{
    
}


/**
 * @brief msgbx hw send msg data
 * 
 * @param msg 
 * @return int32_t 0: is send ok, other: send failed
 */
int32_t msgbx_hw_send_msg(const uint8_t endid, const rw_msg_t *msg)
{
    int32_t ret = -1;
    uint32_t reg_data, time_out_cnt = 0,i;
    uint32_t write_addr;
    msg_head_u write_head;
    uint8_t sem_id = MSG_SEM_INVALID_ID;
    msgbox_data_t *msg_ptr = (msgbox_data_t *) msg;

    endid;
    MSGBX_LOG_DEBUG("msgbx send head=0x%x data[0]=0x%x",msg_ptr->header.data, msg_ptr->payload[0]);
    SchM_Enter_Msgbx_MSGBX_EXCLUSIVE_AREA_00();
    if(0 == msgbx_header_is_valid(MSGBX_BASE_ADDR, &msg_ptr->header))
    {
        SchM_Exit_Msgbx_MSGBX_EXCLUSIVE_AREA_00();
        write_addr = MSGBX_BASE_ADDR + MSGBX_END_TXFIFO_OFFSET;
        write_head.data = msg_ptr->header.data;
        write_head.bit.len = (msg_ptr->header.bit.len << 1) + 1; /*the len is 64bit system value.*/
        /*wait tx fifo available*/
        do{
            reg_data = REG_READ32(MSGBX_BASE_ADDR + END_TX_FIFO_AVAILABLE_OFFSET);
            time_out_cnt++;
            if(time_out_cnt > MSGBX_WAIT_TIMEOUT_CNT){
                ret = -2;
                return(ret);
            }
        }while(reg_data == 0);

        /*write msg head*/
        SchM_Enter_Msgbx_MSGBX_EXCLUSIVE_AREA_01();
        MSGBX_LOG_DEBUG("Send data : 0x%x ", msg_ptr->header.data);
        REG_WRITE32(write_addr, write_head.data);
        for (i = 0; i < write_head.bit.len; i++)
        {
            /*write msg payload*/
            if((i+1) == write_head.bit.len)
            {
                sem_id = msgbx_get_sem_lock(msg_ptr->header.bit.pid, msg_ptr->header.bit.cid);
            }
            MSGBX_LOG_STRING("0x%08x ", msg_ptr->payload[i]);
            REG_WRITE32(write_addr, msg_ptr->payload[i]);
            if(((i+1) == write_head.bit.len) && (sem_id != MSG_SEM_INVALID_ID))
            {
                msgbx_delay_us(1);
                msgbx_release_sem_lock(sem_id);
            }
        }
        SchM_Exit_Msgbx_MSGBX_EXCLUSIVE_AREA_01();
        MSGBX_LOG_STRING("end.\r\n");
        ret = 0;
    }
    else
    {
        SchM_Exit_Msgbx_MSGBX_EXCLUSIVE_AREA_00();
        MSGBX_LOG_DEBUG("header[0x%x] is invalid", msg_ptr->header.data);
        ret = -1;
    }

    return(ret);
}

/**
 * @brief msgbx get a msg from recv fifo
 * 
 * @param msg msg buffer
 * @param fid filter id
 * @return int32_t 
 */
int32_t msgbx_hw_recv_msg(const uint8_t endid, rw_msg_t *msg, const uint8_t fid)
{
    int32_t ret = -1;
    uint32_t reg_data, time_out_cnt, i;
    uint32_t read_addr;
    uint32_t read_len = 0;
    msgbox_data_t *msg_ptr;

    endid;
    if(fid < MSGBX_FILTER_END_NUM)
    {
        if(msgbx_get_rxfifo_status(MSGBX_BASE_ADDR, fid))
        {
            read_addr = MSGBX_BASE_ADDR + MSGBX_END_DE_RXFIFO_OFFSET + fid*0x200;
            msg_ptr = (msgbox_data_t *)msg;
            msg_ptr->header.data = REG_READ32(read_addr);
            read_len = msg_ptr->header.bit.is_64bit ? ((msg_ptr->header.bit.len << 1) + 1) : (msg_ptr->header.bit.len);
            MSGBX_LOG_DEBUG("header=0x%0x,read_len=%d",msg_ptr->header.data,read_len);
            if(read_len <= MSGBX_PAYLOAD_MAX_LEN)
            {
                for (i = 0; i < read_len; i++)
                {
                    msg_ptr->payload[i] = REG_READ32(read_addr);
                }
            }
            ret = 0;
        }
        else
        {
            ret = -1;
        }
    }
    return(ret);
}

/**
 * @brief get timestamp
 * 
 * @param timestamp 
 * @return int32_t 
 */
int32_t msgbx_hw_get_time(uint64_t *timestamp)
{
    return 0;
}

/**
 * @brief enable msgbx statues management
 * 
 * @param cfg 
 * @return int32_t a
 */
int32_t msgbx_hw_sts_mgt_enable(const uint8_t endid, const uint32_t flag)
{
    endid;
    REG_WRITE32(MSGBX_BASE_ADDR  + MSGBX_END_FMU_CSR_OFFSET + MSGBX_FMU_SAFETY_INTR_EN_OFFSET, flag);
    return(0);
}
/**
 * @brief disable msgbx status management
 * 
 * @return int32_t 
 */
int32_t msgbx_hw_sts_mgt_disable(const uint8_t endid)
{
    endid;
    REG_WRITE32(MSGBX_BASE_ADDR + MSGBX_END_FMU_CSR_OFFSET + MSGBX_FMU_SAFETY_INTR_EN_OFFSET,0x00);
    return(0);
}

/**
 * @brief msgbx filter state management config
 * 
 * @param flt_id filter id
 * @param flag 
 * @param flt_thrs 
 * @return int32_t 
 */
int32_t msgbx_hw_flt_mgt_enble(const uint8_t endid, const uint8_t flt_id, const uint8_t flag)
{
    int32_t ret = -1;

    endid;
    if(flt_id < MSGBX_FILTER_END_NUM)
    {
        if(0 == flt_id)
        {
            REG_BIT_SET32(MSGBX_BASE_ADDR + END_ENR_OFFSET, (flag & (END_ENR_DEFAULT_RX_UNDERFLOW_INTR_EN_U32 | 
                        END_ENR_DEFAULT_RX_OVERFLOW_INTR_EN_U32 | END_ENR_TX_OVERFLOW_INTR_EN_U32)));
        }
        else
        {
            REG_BIT_SET32(MSGBX_BASE_ADDR + flt_id*0x200 + END_FILTER_ENR_OFFSET, 
                    (flag & (END_FILTER_ENR_FILTER_UNDERFLOW_INTR_EN_U32 | END_FILTER_ENR_FILTER_OVERFLOW_INTR_EN_U32)));
        }
        ret = 1;
    }

    return(ret);
}

/**
 * @brief disable msgbx filter management
 * 
 * @param flt_id 
 * @return int32_t 
 */
int32_t msgbx_hw_flt_mgt_disable(const uint8_t endid, const uint8_t flt_id)
{
    int32_t ret = -1;

    endid;
    if(flt_id < MSGBX_FILTER_END_NUM)
    {
        if(0 == flt_id)
        {
            REG_BIT_CLEAR32(MSGBX_BASE_ADDR + END_ENR_OFFSET, (END_ENR_DEFAULT_RX_UNDERFLOW_INTR_EN_U32 | 
                        END_ENR_DEFAULT_RX_OVERFLOW_INTR_EN_U32 | END_ENR_TX_OVERFLOW_INTR_EN_U32));
        }
        else
        {
            REG_BIT_CLEAR32(MSGBX_BASE_ADDR + flt_id*0x200 + END_FILTER_ENR_OFFSET, 
                    (END_FILTER_ENR_FILTER_UNDERFLOW_INTR_EN_U32 | END_FILTER_ENR_FILTER_OVERFLOW_INTR_EN_U32));
        }
        ret = 1;
    }

    return(ret);
}

/**
 * @brief msgbx get error msg
 * 
 * @param err_msg 
 * @return int32_t 
 */
int32_t msgbx_hw_get_err_msg(const uint8_t endid, const uint8_t flt_id, msgbx_err_msg_t *err_msg)
{
    uint32_t reg_data = 0;
    
    endid;
    if(MSGBX_FILTER_END_NUM > flt_id)
    {
        if(0 == flt_id){
            reg_data = REG_READ32((MSGBX_BASE_ADDR + MSGBX_END_CONFIG_OFFSET + END_INTRR_OFFSET));
            err_msg->msg = reg_data & (END_INTRR_DEFAULT_RX_UNDERFLOW_INTR_U32 | 
                        END_INTRR_DEFAULT_RX_OVERFLOW_INTR_U32 | END_INTRR_TX_OVERFLOW_INTR_U32);
        }else{
            reg_data = REG_READ32((MSGBX_BASE_ADDR + MSGBX_END_CONFIG_OFFSET + (flt_id * 0x200) + END_FILTER_INTRR_OFFSET));
            err_msg->msg = reg_data & (END_FILTER_INTRR_FILTER_UNDERFLOW_INTR_U32 | 
                        END_FILTER_INTRR_FILTER_OVERFLOW_INTR_U32);
        }
        err_msg->type = 2;
        err_msg->id = 0;
    }
}

 /**
  * @brief  msgbx fault handle
  * 
  * @param type 
  * @param id 
  * @param hdl 
  * @return int32_t 
  */
int32_t msgbx_hw_err_hdl(const uint8_t endid, uint8_t type, uint8_t id, uint32_t hdl)
{

}

/**
 * @brief msgbx node isr callback handle
 * 
 * @param param intr nummber
 */
void msgbx_node_isr_handle(void *param)
{
    uint32_t int_num = (uint32_t)param;

    switch(int_num)
    {
#if defined CORE0_FLAG
    case MSGBX_FILTER_0_INTR:
        msgbx_isr_process(0);
        break;
    case MSGBX_FILTER_1_INTR:
        msgbx_isr_process(1);
        break;
    case MSGBX_FILTER_2_INTR:
        msgbx_isr_process(2);
        break;
    case MSGBX_FILTER_3_INTR:
        msgbx_isr_process(3);
        break;
#elif defined CORE1_FLAG
    case MSGBX_FILTER_4_INTR:
        msgbx_isr_process(0);
        break;
    case MSGBX_FILTER_5_INTR:
        msgbx_isr_process(1);
        break;
    case MSGBX_FILTER_6_INTR:
        msgbx_isr_process(2);
        break;
    case MSGBX_FILTER_7_INTR:
        msgbx_isr_process(3);
        break;
#else
        #error "CORE[x]_FLAG need define, x=0/1/2"
#endif
    default:
        MSGBX_LOG_DEBUG("unsupport isr:%d\n", int_num); 
        uint32 fid = int_num - MSGBX_FILTER_0_INTR;
        uint32_t reg_base = MSGBX_BASE_ADDR + (fid * 0x200);

        uint32_t reg_data = REG_READ32(reg_base+END_INTRR_OFFSET);
        REG_WRITE32((reg_base+END_STATUSR_OFFSET), reg_data);

        reg_data = REG_READ32(reg_base + END_FILTER_INTRR_OFFSET);
        REG_WRITE32((reg_base+END_FILTER_STATUSR_OFFSET), reg_data);
        break;
    }
}

/**
 * @brief msgbox_filter_0_isr_handle
 * 
 */
ISR(msgbox_filter_0_isr_handle)
{
    msgbx_isr_process(0);
}

/**
 * @brief msgbox_filter_1_isr_handle
 * 
 */
ISR(msgbox_filter_1_isr_handle)
{
    msgbx_isr_process(0);
}

/**
 * @brief msgbox_filter_2_isr_handle
 * 
 */
ISR(msgbox_filter_2_isr_handle)
{
    msgbx_isr_process(0);
}

/**
 * @brief msgbox_filter_3_isr_handle
 * 
 */
ISR(msgbox_filter_3_isr_handle)
{
    msgbx_isr_process(0);
}

libipc_hw_compat_ops_t msg_hw_ops = {
    .ipc_hw_init = msgbx_hw_init,
    .ipc_hw_deinit = msgbx_hw_deinit,
    .ipc_hw_get_info = msgbx_hw_get_info,
    .ipc_hw_set_flt_cfg = msgbx_hw_set_flt_cfg,
    .ipc_hw_get_flt_info = msgbx_hw_get_flt_info,
    .ipc_hw_clr_flt_cfg = msgbx_hw_clr_flt_cfg,
    .ipc_hw_send_msg = msgbx_hw_send_msg,
    .ipc_hw_get_msg = msgbx_hw_recv_msg,

    .ipc_hw_get_time = msgbx_hw_get_time,
    .ipc_hw_sts_mgt_enble = msgbx_hw_sts_mgt_enable,
    .ipc_hw_sts_mgt_disable = msgbx_hw_sts_mgt_disable,
    .ipc_hw_flt_mgt_enble = msgbx_hw_flt_mgt_enble,
    .ipc_hw_flt_mgt_disable = msgbx_hw_flt_mgt_disable,
    .ipc_hw_get_err_msg = msgbx_hw_get_err_msg,
    .ipc_hw_err_hdl = msgbx_hw_err_hdl,
};